/**
 * 
 */
package sk.stuba.fiit.foo07.genex.export;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.sql.Connection;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;

import sk.stuba.fiit.foo07.genex.beans.Answer;
import sk.stuba.fiit.foo07.genex.beans.ExportAnswer;
import sk.stuba.fiit.foo07.genex.beans.ExportPicture;
import sk.stuba.fiit.foo07.genex.beans.ExportQuestion;
import sk.stuba.fiit.foo07.genex.beans.Picture;
import sk.stuba.fiit.foo07.genex.beans.QuestionPoints;
import sk.stuba.fiit.foo07.genex.beans.QuestionType;
import sk.stuba.fiit.foo07.genex.beans.Test;
import sk.stuba.fiit.foo07.genex.dao.AnswerDaoDerby;
import sk.stuba.fiit.foo07.genex.dao.PictureDaoDerby;
import sk.stuba.fiit.foo07.genex.dao.QuestionDaoDerby;
import sk.stuba.fiit.foo07.genex.dao.QuestionTypeDaoDerby;
import sk.stuba.fiit.foo07.genex.dao.TestDaoDerby;
import sk.stuba.fiit.foo07.genex.dao.UserDaoDerby;

/**
 * @author Radu
 */
/**
 * For tag description see User Guide.
 */

public class ExportLatex extends Export {
    public ArrayList<QuestionPoints> questPointsList;
    public ArrayList<ExportQuestion> exQuestList;
    public ArrayList<ExportAnswer> exAnsList;
    public ArrayList<ExportPicture> exPictureList;
    private PictureDaoDerby PDD;
    private QuestionDaoDerby QDD;
    private AnswerDaoDerby ADD;
    private UserDaoDerby UDD;
    private QuestionTypeDaoDerby QTDD;
    private TestDaoDerby QTD;
    public ArrayList<Answer> ansList;
    private String instructions = null;
    private Test toExport;
    private boolean permutateAnswers = true;
    private ExportQuestion eq;
    private QuestionPoints qp;
    private String text;
    private String points;
    private String template;
    private String answer;
    private String date;
    private ExportAnswer ea;
    private Picture picture;
    private String pointsFloat;
    private Integer sumaInt = 0;
    private Float sumaFloat = (float) 0.0;
    private String group = null;
    private DecimalFormat df;
    private Iterator<Picture> it3;

    @Override
    public void exportTest(Integer testId, Connection con) throws IOException {

        QDD = new QuestionDaoDerby(con);
        ADD = new AnswerDaoDerby(con);
        QTDD = new QuestionTypeDaoDerby(con);
        PDD = new PictureDaoDerby(con);
        QTD = new TestDaoDerby(con);
        UDD = new UserDaoDerby(con);
        // First prepare all data for velocity template

        try {
            toExport = QTD.getTestByID(testId);
        } catch (Exception e) {
        }
        questPointsList = toExport.getQuestionPoints();
        exQuestList = new ArrayList<ExportQuestion>(questPointsList.size());

        sumaInt = 0;
        sumaFloat = (float) 0.0;
        Iterator<QuestionPoints> it = questPointsList.iterator();
        while (it.hasNext()) {
            qp = it.next();

            try {
                text = QDD.getQuestionByID(qp.getQuestionId()).getText();
            } catch (SQLException e) {

                e.printStackTrace();
            }
            points = new String(new Integer(qp.getPoints().intValue())
                    .toString());
            sumaInt += qp.getPoints().intValue();

            df = new DecimalFormat("0.#");

            pointsFloat = df.format(qp.getPoints());
            sumaFloat += qp.getPoints();

            //TODO 
            //pridane odchytavanie vynimky (palo)
            try {
                ansList = ADD.getAnswersByQuestionID(qp.getQuestionId());
            } catch (Exception e) {
            }
            if (permutateAnswers)
                Collections.shuffle(ansList);
            exAnsList = new ArrayList<ExportAnswer>(5);
            Iterator<Answer> it2 = ansList.iterator();
            while (it2.hasNext()) {
                Answer a = it2.next();
                answer = a.getText();
                boolean isCorrect = a.getIsCorrect();

                ea = new ExportAnswer(answer, isCorrect);
                exAnsList.add(ea);
            }
            ArrayList<Picture> pictureList = null; //pozor mozna chyba
            //TODO 
            //pridane odchytavanie vynimky (palo)
            try {
                pictureList = PDD.getPicturesByQuestionID(qp.getQuestionId());
                exPictureList = new ArrayList<ExportPicture>(pictureList.size());
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (!pictureList.isEmpty()) {

                Iterator<Picture> it4 = pictureList.iterator();
                while (it4.hasNext()) {
                    ExportPicture ep = new ExportPicture(it4.next());
                    if (ep.getMIMETYPE().startsWith("tex")
                            || ep.getMIMETYPE().startsWith("ltx"))
                        ep.setTextPicture(true);
                    exPictureList.add(ep);
                }
            }
            eq = new ExportQuestion(qp.getQuestionId(), text, points,
                    pointsFloat, exAnsList, exPictureList);
            exQuestList.add(eq);

        }
        // Now write all images of test from database to files
        try {
            ArrayList<Picture> pictureList = PDD.getPicturesByTestID(toExport
                    .getTestID());
            it3 = pictureList.iterator();

            while (it3.hasNext()) {

                picture = it3.next();

                FileOutputStream fos = new FileOutputStream(new File(picture
                        .getName()));

                fos.write(picture.getContent());
                fos.close();
                // pridat bufferovanie..
            }
        } catch (SQLException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        VelocityEngine ve = new VelocityEngine();
        /*  next, get the Template  */
        Template t;
        try {

            ve.init();
            t = ve.getTemplate("templates"
                    + System.getProperty("file.separator") + template, "UTF8");

            /*  create a context and add data */
            VelocityContext context = new VelocityContext();

            context.put("FILEHEADER", this.getFileHeader());
            context.put("TESTHEADER", this.getTestHeader());
            context.put("NAMETABLE", this.getNameTable());
            context.put("ANSWERTABLE", this.getAnswerTable(false));
            context.put("TESTNAME", this.getTestName());
            context.put("TESTSUBJECT", this.getSubject());
            context.put("TESTGROUP", this.getGroupLatex());
            context.put("TESTCREATOR", this.getUser());
            context.put("TESTDATE", this.getTestDate());
            context.put("TESTINSTRUCTIONS", this.getHelloMsg());
            context.put("CORRECTANSWERS", this.getAnswerTable(true));
            context.put("TOTALPOINTS", this.sumaInt.toString());
            context.put("TOTALPOINTSFLOAT", df.format(sumaFloat));

            context.put("QUESTIONLIST", exQuestList);

            /* now render the template into a StringWriter */
            StringWriter writer = new StringWriter();
            t.merge(context, writer);
            /* show the World */
            //pw = new PrintWriter(new FileOutputStream(new File("test.tex")));
            //pw.println(writer.toString());
            FileOutputStream f = new FileOutputStream(super.getOutputFilename());

            OutputStreamWriter osw = new OutputStreamWriter(f, Charset
                    .forName("UTF8"));

            osw.write(writer.toString());
            osw.flush();
            osw.close();

            // Now get rid of text picture files
            ArrayList<Picture> pictureList = PDD.getPicturesByTestID(toExport
                    .getTestID());

            Iterator<ExportQuestion> exQIT = exQuestList.iterator();
            while (exQIT.hasNext()) {
                Iterator<ExportPicture> it3a = exQIT.next().getPICTURELIST()
                        .iterator();
                while (it3a.hasNext()) {

                    ExportPicture picture1 = it3a.next();
                    if (picture1.getMIMETYPE().startsWith("tex")
                            || picture1.getMIMETYPE().startsWith("ltx")) {

                        File textPicture = new File(picture1.getNAME());
                        textPicture.delete();
                    }

                }
            } // pw.flush();
        } catch (ResourceNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ParseErrorException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public String getTestDate() {
        if (date.length() == 0)
            return new String("");
        else
            return new String("Dátum: " + date + "\\\\ \n");
    }

    public String getTestName() {
        StringBuilder testName = new StringBuilder();
        testName.append("\\textbf{");
        testName.append(toExport.getName());
        testName.append("}\\\\ \n");
        return (testName.toString());
    }

    public String getSubject() {
        return (new String("Predmet: " + toExport.getSubject() + "\\\\ \n"));

    }

    public String getGroupLatex() {
        if (group.length() == 0) {
            return (new String(""));
        } else {
            return (new String("Skupina: " + group + "\\\\ \n"));
        }

    }

    //TODO pridany catch blok, pozret logiku metody
    public String getUser() {

        try {
            return new String("Vyucujuci: "
                    + UDD.getTUserByID(toExport.getUserID()) + "\\\\ \n");
        } catch (Exception e) {
            return null;
        }
    }

    public String getTestHeader() {
        StringBuilder testHeader = new StringBuilder();

        testHeader.append(getTestName());
        testHeader.append(getSubject());
        testHeader.append(getGroupLatex());
        testHeader.append(getUser());
        testHeader.append(getHelloMsg());

        return testHeader.toString();
    }

    public String getNameTable() {
        StringBuilder testNameTable = new StringBuilder();
        testNameTable.append("\\begin{tabular}{|l|l|l|l|} \\hline \n");
        testNameTable
                .append("\\textbf{Priezvisko:} & \\mbox{\\hspace{3cm}}& \\textbf{Os. číslo} & \\textbf{Body}\\\\[2mm] \\hline \n");
        testNameTable.append("\\textbf{Meno:} &  &  &  \\\\[2mm] \\hline \n");
        testNameTable.append("\\end{tabular} \n");
        testNameTable.append("\\vspace{2mm} \\ \n");
        return testNameTable.toString();
    }

    public String getHelloMsg() {
        if (instructions.length() == 0)
            return new String("");
        else
            return new String(instructions + "\\ \n \\vspace{2mm} \n");
    }

    public String getFileHeader() {
        return new String("% " + toExport.getName()
                + "\n% Vygenerovane systemom Genex");
    }

    private String writeAnswerTableToFile(int[] QuestionOrder,
            Hashtable<Integer, ArrayList<Integer>> ChoiceAnswers,
            int maxPossibleAnswers, boolean answers) {

        int i, j;
        int tableStep = 5; //after each 'tableStep' rows there's a tiny jump in the table
        StringBuilder table = new StringBuilder();

        //we have no 'choice' question
        if (ChoiceAnswers.size() == 0) {
            return new String(
                    "%No AnswerTable: none single choice, multi choice or True-false question");

        }

        //Answertable beginning
        table.append("\\begin{tabular}{|c||");

        //create column separators
        for (i = 0; i < maxPossibleAnswers + 1; i++)
            table.append("c|");

        table.append("} \\hline \n ");

        //answers 'a' 'b' 'c' ...
        for (i = 97; i < maxPossibleAnswers + 97; i++)
            table.append(" & " + (char) i);

        //formating stuff
        table.append(" \\\\ \\hline\\hline \n");

        //write answers or just empty space into table fields
        for (i = 0; i < ChoiceAnswers.size(); i++) {
            table.append((QuestionOrder[i]) + " ");
            for (j = 0; j < maxPossibleAnswers; j++) {
                if (answers && ChoiceAnswers.get(i).contains(j))
                    table.append("&  X"); //write correct answers
                else
                    table.append("&   "); //write empty AnswerTable
            }

            //after each tableStep-rows make a double hline and
            //don't make a double hline after the last line
            if ((i + 1) % tableStep == 0 && (i + 1) != ChoiceAnswers.size())
                table.append("\\\\ \\hline \\hline");
            else
                table.append("\\\\ \\hline");

            table.append('\n');
        }

        //end the table
        table.append("\\end{tabular}");

        return new String(table.toString());

    }

    public String getAnswerTable(boolean withCorrectAnswers) {

        QuestionType qt;
        ExportQuestion question;
        int i = 0;
        int j = 0;
        int numberOfChoiceQuestions = 0;
        int maxPossibleAnswers = 0;
        int PossibleAnswers = 0;
        Hashtable<Integer, ArrayList<Integer>> ChoiceAnswers = new Hashtable<Integer, ArrayList<Integer>>();
        ArrayList<Integer> correctAnswers;
        ArrayList<ExportAnswer> a_list;
        int[] QuestionOrder;

        QuestionOrder = new int[exQuestList.size()];

        for (i = 0; i < exQuestList.size(); i++) {
            question = exQuestList.get(i);
            //TODO
            //vynimky
            qt = null;
            try {
                qt = QTDD.getQuestionTypeByQuestionID(question.getQuestionID());
            } catch (Exception e) {
            }

            if ("Single choice".equals(qt.getName())
                    || "Multi choice".equals(qt.getName())
                    || "True/False".equals(qt.getName())) {

                a_list = question.getANSWERLIST();

                //get maximum possible answers in test 
                PossibleAnswers = a_list.size();

                if (PossibleAnswers > maxPossibleAnswers)
                    maxPossibleAnswers = PossibleAnswers;

                //remember order of question
                //questions start from 1 not 0
                QuestionOrder[numberOfChoiceQuestions] = i + 1;

                correctAnswers = new ArrayList<Integer>();

                //remember correct answers order
                for (j = 0; j < a_list.size(); j++)
                    if (a_list.get(j).isCorrect()) {
                        correctAnswers.add(j);
                    }

                //map question order to correct answer order 
                ChoiceAnswers.put(numberOfChoiceQuestions, correctAnswers);
                numberOfChoiceQuestions++;

            }

        }

        //write table to output
        return new String(writeAnswerTableToFile(QuestionOrder, ChoiceAnswers,
                maxPossibleAnswers, withCorrectAnswers));

    }

    public String getInstructions() {
        return instructions;
    }

    public void setInstructions(String helloMesage) {
        this.instructions = helloMesage;
    }

    public boolean isPermutateAnswers() {
        return permutateAnswers;
    }

    public void setPermutateAnswers(boolean permutateAnswers) {
        this.permutateAnswers = permutateAnswers;
    }

    public String getTemplate() {
        return template;
    }

    public void setTemplate(String template) {
        this.template = template;
    }

    public void setGroup(String group) {
        this.group = group;
    }

    public String getGroup() {
        return group;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

}
